#include "Kernel_PCH.h"
#include "Kernel.h"

/*--------------------------------------------------------------------------*/

IKernel* g_Kernel = NULL;

/*--------------------------------------------------------------------------*/

CKernel::CKernel() :
	m_continueLoop(true)
{
	if (SDL_Init(SDL_INIT_EVERYTHING) != 0) 
	{
		__Crash("Unable to initialize SDL: %s", SDL_GetError());
	}

	__Info("%s", "SDL Initialized!");

	m_threadsVectorMutex = CreateNewMutex();
}

/*--------------------------------------------------------------------------*/

CKernel::~CKernel()
{
	delete m_threadsVectorMutex;

	SDL_Quit();
}

/*--------------------------------------------------------------------------*/

void CKernel::CreateGameWindow(const std::string& title, uint width, uint height, bool fullscreen, bool vsync)
{
	m_title			= title;
	m_resx			= width;
	m_resy			= height;
	m_fullscreen	= fullscreen;
	m_vsync			= vsync;

	__Info("Creating window. Resolution: %dx%d, Fullscreen: %d, VSync: %d", m_resx, m_resy, m_fullscreen, m_vsync);

	uint windowflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;

	if (fullscreen)
	{
		windowflags |= SDL_WINDOW_FULLSCREEN;
	}

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);

	m_window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, windowflags);

	if (m_window == NULL)
	{
		__Crash("Unable to create window: %s", SDL_GetError());
		return;
	}

	m_glcontext = SDL_GL_CreateContext(m_window);

	if (m_glcontext == NULL)
	{
		__Crash("Unable to create GL context: ", SDL_GetError());
		return;
	}

	SDL_GL_MakeCurrent(m_window, m_glcontext);

	if (m_vsync)
	{
		if (SDL_GL_SetSwapInterval(1) != 0)
		{
			__Crash("Unable to set vsync: %s", SDL_GetError());
		}
	}
}

/*--------------------------------------------------------------------------*/

void CKernel::Delay( int miliseconds )
{
	SDL_Delay(miliseconds);
}

/*--------------------------------------------------------------------------*/

void CKernel::InifityLoop()
{
	uint i = 0;
	std::string rstats;

	bool debug = false;
	ITimer* timer = CreateNewTimer();
	ITimer* deltaTimer = CreateNewTimer();

	while (m_continueLoop)
	{
		deltaTimer->ResetTimer();

		if (debug == false && g_Input->GetKeyState(SDLK_F1))
		{
			debug = true;
		}

#define PERF_COUNTER(a) timer->ResetTimer(); a; __CondInfo(debug, #a ": %f ms", timer->GetTime_ms());
		
		PERF_COUNTER(StartThreads(EThread::Synchronize));

		PERF_COUNTER(g_Input->Capture());
		PERF_COUNTER(WaitThreads(EThread::SynchronizeBeforeRendering));

		PERF_COUNTER(g_Renderer->RenderFrame());

		PERF_COUNTER(StartThreads(EThread::SynchronizeBeforeRendering));
		PERF_COUNTER(SwapBuffers());

		PERF_COUNTER(WaitThreads(EThread::Synchronize));

#undef PERF_COUNTER

		if (debug && g_Input->GetKeyState(SDLK_F1) == false)
		{
			debug = false;
		}

		m_deltaTime = deltaTimer->GetTime_ms();

		rstats = "Stats: A=" + std::to_string(g_Renderer->GetRenderStats().DrawArrays)
					+ "E=" + std::to_string(g_Renderer->GetRenderStats().DrawElements)
					+ "AI=" + std::to_string(g_Renderer->GetRenderStats().DrawArraysInstanced)
					+ "EI=" + std::to_string(g_Renderer->GetRenderStats().DrawElementsInstanced);
		g_UserInterface->AddMessage(rstats);
		g_Renderer->ResetRenderStats();
	}

	delete deltaTimer;
	delete timer;
}

/*--------------------------------------------------------------------------*/

void CKernel::StopThreads()
{
// 	auto iter = m_threads.begin();
// 
// 	while((iter = m_threads.begin()) != m_threads.end())
// 	{
// 		iter->first->StopThread();
// 		iter->first->Wait(NULL);
// 	}
}

/*--------------------------------------------------------------------------*/

void CKernel::SwapBuffers()
{
	SDL_GL_SwapWindow(m_window);
}

/*--------------------------------------------------------------------------*/

ITimer* CKernel::CreateNewTimer()
{
	return new Timer();
}

/*--------------------------------------------------------------------------*/

IMutex* CKernel::CreateNewMutex()
{
	return new Mutex();
}

/*--------------------------------------------------------------------------*/

void CKernel::Close()
{
	m_continueLoop = false;
}

/*--------------------------------------------------------------------------*/

uint CKernel::GetTicks()
{
	return SDL_GetTicks();
}

/*--------------------------------------------------------------------------*/

void CKernel::LockThread( IThread* thread )
{
// 	m_threadsVectorMutex->Lock();
// 	m_threads.push_back(thread);
// 	m_threadsVectorMutex->Unlock();

	thread->LockCond();
}

/*--------------------------------------------------------------------------*/

void CKernel::RegisterThread( IThread* thread )
{
	m_threadsVectorMutex->Lock();
	m_threads.push_back(thread);
	m_threadsVectorMutex->Unlock();
}

/*--------------------------------------------------------------------------*/

void CKernel::UnregisterThread( IThread* thread )
{
	m_threadsVectorMutex->Lock();
	m_threads.push_back(thread);
	m_threadsVectorMutex->Unlock();
}

/*--------------------------------------------------------------------------*/

void CKernel::StartThreads( EThread::Type type )
{
	for (auto iter = m_threads.begin(); iter != m_threads.end(); ++iter)
	{
		if ((*iter)->GetSyncType() == type)
		{
			(*iter)->UnlockCond();
		}
	}
}

/*--------------------------------------------------------------------------*/

void CKernel::WaitThreads( EThread::Type type )
{
	for (auto iter = m_threads.begin(); iter != m_threads.end(); ++iter)
	{
		if ((*iter)->GetSyncType() == type)
		{
			(*iter)->LockMutex();
			(*iter)->UnlockMutex();
		}
	}
}

/*--------------------------------------------------------------------------*/

float CKernel::GetDeltaTime()
{
	return m_deltaTime;
}

/*--------------------------------------------------------------------------*/
